﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Linq;
using RTMBindings.Responses;
using RTMBindings.Utilities;
using System.IO;

namespace RTMBindings
{
    public class RTMBindings
    {
        protected const string UriBase = "https://api.rememberthemilk.com/services/rest/?method=";

        protected RTMBindings() { }

        protected static void RequestString<T>(string secret, UriBuilder uri, Action<T> callback, Action<Exception> failedCallback) where T : RTMResponse
        {
            var client = new WebClient();
            client.DownloadStringCompleted += (sender, args) =>
            {
                RTMResponse res;
                if (args.Error != null)
                {
                    try
                    {
                        var ress = args.Result; // If it's a web error, this will throw
                        failedCallback(args.Error);
                        return;
                    }
                    catch (WebException ex)
                    {
                        var resp = ex.Response as HttpWebResponse;
                        if (resp != null)
                        {
                            if (resp.ContentLength > 0)
                            {
                                var respStream = new StreamReader(resp.GetResponseStream());
                                var content = respStream.ReadToEnd();
                                res = RTMResponseFactory.ParseResponse<T>(content);
                            }
                            else
                            {
                                failedCallback(args.Error);
                                return;
                            }
                        }
                        else
                        {
                            failedCallback(args.Error);
                            return;
                        }
                    }
                }
                else
                {
                    res = RTMResponseFactory.ParseResponse<T>(args.Result);
                }

                if (res.Status == RTMResponseStatus.Success)
                {
                    callback(res as T);
                }
                else
                {
                    failedCallback(new RTMException((RTMFailResponse)res));
                }

            };
            SignParameters(uri, secret);
            client.DownloadStringAsync(uri.Uri);
        }

        protected static void SignParameters(UriBuilder uri, string sharedSecret)
        {
            var query = uri.Query.Substring(1);
            var arguments = query.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries);
            var splitArgs = arguments.Select(a => a.Split(new[] { "=" }, StringSplitOptions.None)).ToDictionary(
                s => s[0], s => s[1]);
            var sig = SignParameters(splitArgs, sharedSecret);
            uri.Query = string.Format("{0}&api_sig={1}", uri.Query.Substring(1), sig.ToLowerInvariant());
        }

        private static string SignParameters(IEnumerable<KeyValuePair<string, string>> parameters, string sharedSecret)
        {
            var builder = new StringBuilder();
            builder.Append(sharedSecret);
            foreach (var kvp in parameters.OrderBy(p => p.Key))
            {
                builder.Append(kvp.Key);

                builder.Append(kvp.Value.Replace("%20", " "));
            }

            return MD5Core.GetHashString(builder.ToString());
        }
    }
}
